import * as fs from 'fs';
import * as path from 'path';
import { ApiEndpoint, ApiParameter, ApiExample } from '../extension';

export class JavaParser {
  async parseFile(filePath: string): Promise<ApiEndpoint[]> {
    const content = fs.readFileSync(filePath, 'utf8');
    const lines = content.split('\n');
    const endpoints: ApiEndpoint[] = [];

    for (let i = 0; i < lines.length; i++) {
      const line = lines[i].trim();
      
      // Look for Spring Boot annotations
      if (line.includes('@RequestMapping') || 
          line.includes('@GetMapping') || 
          line.includes('@PostMapping') || 
          line.includes('@PutMapping') || 
          line.includes('@DeleteMapping') ||
          line.includes('@PatchMapping')) {
        
        const endpoint = await this.parseEndpoint(lines, i, filePath);
        if (endpoint) {
          endpoints.push(endpoint);
        }
      }
    }

    return endpoints;
  }

  private async parseEndpoint(lines: string[], startLine: number, filePath: string): Promise<ApiEndpoint | null> {
    const line = lines[startLine];
    let method = 'GET';
    let path = '';
    let description = '';
    let parameters: ApiParameter[] = [];
    let returnType = 'void';
    let returnDescription = '';

    // Extract HTTP method and path from annotation
    if (line.includes('@GetMapping')) {
      method = 'GET';
      path = this.extractPath(line);
    } else if (line.includes('@PostMapping')) {
      method = 'POST';
      path = this.extractPath(line);
    } else if (line.includes('@PutMapping')) {
      method = 'PUT';
      path = this.extractPath(line);
    } else if (line.includes('@DeleteMapping')) {
      method = 'DELETE';
      path = this.extractPath(line);
    } else if (line.includes('@PatchMapping')) {
      method = 'PATCH';
      path = this.extractPath(line);
    } else if (line.includes('@RequestMapping')) {
      method = this.extractMethod(line);
      path = this.extractPath(line);
    }

    // Look for method signature in next few lines
    let methodName = '';
    for (let i = startLine + 1; i < Math.min(startLine + 10, lines.length); i++) {
      const methodLine = lines[i];
      if (methodLine.includes('public') && methodLine.includes('(')) {
        methodName = this.extractMethodName(methodLine);
        returnType = this.extractReturnType(methodLine);
        parameters = this.extractParameters(lines, i, methodLine);
        break;
      }
    }

    // Look for JavaDoc comments above the annotation
    for (let i = startLine - 1; i >= Math.max(0, startLine - 10); i--) {
      const commentLine = lines[i];
      if (commentLine.includes('/**')) {
        description = this.extractJavaDocDescription(lines, i);
        break;
      }
    }

    if (!methodName) {
      console.log('No method name 123');
      return null;
    }

    const examples = this.generateExamples(method, path, parameters, returnType);

    return {
      name: methodName,
      path,
      method,
      description: description || `${method} endpoint for ${methodName}`,
      parameters,
      returnType,
      returnDescription,
      examples,
      filePath,
      lineNumber: startLine + 1
    };
  }

  private extractPath(line: string): string {
    const pathMatch = line.match(/value\s*=\s*["']([^"']+)["']/);
    if (pathMatch) {
      return pathMatch[1];
    }
    
    const pathMatch2 = line.match(/["']([^"']+)["']/);
    if (pathMatch2) {
      return pathMatch2[1];
    }
    
    return '/';
  }

  private extractMethod(line: string): string {
    if (line.includes("method = RequestMethod.GET")) return 'GET';
    if (line.includes("method = RequestMethod.POST")) return 'POST';
    if (line.includes("method = RequestMethod.PUT")) return 'PUT';
    if (line.includes("method = RequestMethod.DELETE")) return 'DELETE';
    if (line.includes("method = RequestMethod.PATCH")) return 'PATCH';
    return 'GET';
  }

  private extractMethodName(line: string): string {
    const match = line.match(/(\w+)\s*\(/);
    return match ? match[1] : 'unknown';
  }

  private extractReturnType(line: string): string {
    const match = line.match(/public\s+([^\s]+)/);
    return match ? match[1] : 'void';
  }

  private extractParameters(lines: string[], methodLineIndex: number, methodLine: string): ApiParameter[] {
    const params: ApiParameter[] = [];
    const paramMatch = methodLine.match(/\(([^)]+)\)/);
    
    if (paramMatch) {
      const paramString = paramMatch[1];
      const paramList = paramString.split(',').map(p => p.trim());
      
      for (const param of paramList) {
        if (param && param !== '') {
          const paramInfo = this.parseParameterInfo(param, lines, methodLineIndex);
          if (paramInfo) {
            params.push(paramInfo);
          }
        }
      }
    }
    
    return params;
  }

  private parseParameterInfo(paramString: string, lines: string[], methodLineIndex: number): ApiParameter | null {
    // Extract parameter annotations and type information
    const annotationMatch = paramString.match(/(@\w+(?:\([^)]*\))?\s*)*([^\s]+)\s+(\w+)/);
    if (!annotationMatch) return null;

    const annotations = annotationMatch[1] || '';
    const type = annotationMatch[2];
    const name = annotationMatch[3];

    // Determine parameter source and extract additional info
    let source = 'body';
    let description = `Parameter ${name} of type ${type}`;
    let required = true;
    let defaultValue: string | undefined;

    // Check for Spring annotations
    if (annotations.includes('@PathVariable')) {
      source = 'path';
      const pathVarMatch = annotations.match(/@PathVariable\(["']([^"']+)["']\)/);
      if (pathVarMatch) {
        description = `Path variable: ${pathVarMatch[1]}`;
      } else {
        description = `Path variable: ${name}`;
      }
    } else if (annotations.includes('@RequestParam')) {
      source = 'query';
      const reqParamMatch = annotations.match(/@RequestParam\(["']([^"']+)["']\)/);
      if (reqParamMatch) {
        description = `Query parameter: ${reqParamMatch[1]}`;
      } else {
        description = `Query parameter: ${name}`;
      }
      
      // Check for required=false
      if (annotations.includes('required=false')) {
        required = false;
      }
      
      // Check for defaultValue
      const defaultMatch = annotations.match(/defaultValue\s*=\s*["']([^"']+)["']/);
      if (defaultMatch) {
        defaultValue = defaultMatch[1];
      }
    } else if (annotations.includes('@RequestBody')) {
      source = 'body';
      description = `Request body: ${type}`;
    } else if (annotations.includes('@RequestHeader')) {
      source = 'header';
      const headerMatch = annotations.match(/@RequestHeader\(["']([^"']+)["']\)/);
      if (headerMatch) {
        description = `Header: ${headerMatch[1]}`;
      } else {
        description = `Header: ${name}`;
      }
    } else if (annotations.includes('@RequestAttribute')) {
      source = 'attribute';
      description = `Request attribute: ${name}`;
    } else if (annotations.includes('@SessionAttribute')) {
      source = 'session';
      description = `Session attribute: ${name}`;
    }

    // Look for parameter documentation in JavaDoc
    const paramDoc = this.extractParameterDocumentation(lines, methodLineIndex, name);
    if (paramDoc) {
      description = paramDoc;
    }

    return {
      name,
      type,
      description,
      required,
      defaultValue,
      source
    };
  }

  private extractParameterDocumentation(lines: string[], methodLineIndex: number, paramName: string): string | null {
    // Look for @param documentation in JavaDoc above the method
    for (let i = methodLineIndex - 1; i >= Math.max(0, methodLineIndex - 20); i--) {
      const line = lines[i];
      if (line.includes('/**')) {
        // Search through the JavaDoc block
        for (let j = i; j < lines.length; j++) {
          const docLine = lines[j];
          if (docLine.includes('*/')) break;
          
          const paramMatch = docLine.match(new RegExp(`@param\\s+${paramName}\\s+(.+)`));
          if (paramMatch) {
            return paramMatch[1].trim();
          }
        }
        break;
      }
    }
    return null;
  }

  private extractJavaDocDescription(lines: string[], startLine: number): string {
    let description = '';
    
    for (let i = startLine; i < lines.length; i++) {
      const line = lines[i];
      if (line.includes('*/')) break;
      
      if (line.includes('*')) {
        const content = line.replace(/^\s*\*\s*/, '').trim();
        if (content && !content.startsWith('@')) {
          description += content + ' ';
        }
      }
    }
    
    return description.trim();
  }

  private generateExamples(method: string, path: string, parameters: ApiParameter[], returnType: string): ApiExample[] {
    const examples: ApiExample[] = [];

    // cURL example
    let curlExample = `curl -X ${method} "http://localhost:8080${path}"`;
    
    // Add headers if needed
    const headers: string[] = [];
    const bodyParams: ApiParameter[] = [];
    const queryParams: ApiParameter[] = [];
    const pathParams: ApiParameter[] = [];

    parameters.forEach(param => {
      if (param.source === 'header') {
        headers.push(`-H "${param.name}: value"`);
      } else if (param.source === 'body') {
        bodyParams.push(param);
      } else if (param.source === 'query') {
        queryParams.push(param);
      } else if (param.source === 'path') {
        pathParams.push(param);
      }
    });

    // Add query parameters
    if (queryParams.length > 0) {
      const queryString = queryParams.map(p => `${p.name}=value`).join('&');
      curlExample = curlExample.replace('"', `"?${queryString}"`);
    }

    // Add headers
    if (headers.length > 0) {
      curlExample += ` \\\n  ${headers.join(' \\\n  ')}`;
    }

    // Add body for POST/PUT/PATCH
    if (method !== 'GET' && bodyParams.length > 0) {
      curlExample += ` \\\n  -H "Content-Type: application/json" \\\n  -d '{\n`;
      const paramData = bodyParams.map(p => `    "${p.name}": "value"`).join(',\n');
      curlExample += paramData + '\n  }';
    }

    examples.push({
      title: 'cURL Request',
      description: 'Example cURL request for this endpoint',
      code: curlExample,
      language: 'bash'
    });

    // Java example
    let javaExample = `// Using RestTemplate\nRestTemplate restTemplate = new RestTemplate();\n`;
    
    if (method === 'GET') {
      javaExample += `ResponseEntity<${returnType}> response = restTemplate.getForEntity("http://localhost:8080${path}", ${returnType}.class);`;
    } else {
      javaExample += `HttpHeaders headers = new HttpHeaders();\nheaders.setContentType(MediaType.APPLICATION_JSON);\n`;
      
      if (bodyParams.length > 0) {
        javaExample += `// Create request body\nMap<String, Object> requestBody = new HashMap<>();\n`;
        bodyParams.forEach(p => {
          javaExample += `requestBody.put("${p.name}", "value");\n`;
        });
        javaExample += `HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, headers);\n`;
      } else {
        javaExample += `HttpEntity<String> request = new HttpEntity<>(headers);\n`;
      }
      javaExample += `ResponseEntity<${returnType}> response = restTemplate.exchange("http://localhost:8080${path}", HttpMethod.${method}, request, ${returnType}.class);`;
    }

    examples.push({
      title: 'Java RestTemplate',
      description: 'Example using Spring RestTemplate',
      code: javaExample,
      language: 'java'
    });

    return examples;
  }
} 